﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DailyDiet.Models;
using SQLite;

namespace DailyDiet.Services.Impl
{
    public class DietStorageService : IDietStorageService {
        public const string DietDBName = "dailydiet.db";

        private static Dictionary<int, Diet> _dietCache =
            new Dictionary<int, Diet>();

        public static readonly string DBPath = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder
                .LocalApplicationData), DietDBName);

        private IPreferenceStorage _preference;

        private SQLiteAsyncConnection _connection;

        public SQLiteAsyncConnection Connection =>
            _connection ?? (_connection = new SQLiteAsyncConnection(DBPath));

        public async Task InitializeAsync() {
            using (var embeddedResourceStream = 
                Assembly.GetExecutingAssembly().GetManifestResourceStream(DietDBName))
            using (var fileStream = 
                new FileStream(DBPath, FileMode.OpenOrCreate)) {
                if (embeddedResourceStream == null || fileStream == null) {
                    throw new Exception();
                }
                await embeddedResourceStream.CopyToAsync(fileStream);
                _preference.Set(DietStorageConstants.VersionKey, DietStorageConstants.VERSION);
            }
        }

        public bool IsInitialized() =>
            _preference
                .Get(DietStorageConstants.VersionKey,
                    DietStorageConstants.DEFAULTVERSION)
                .Equals(DietStorageConstants.VERSION);

        public async Task<Diet> GetDietAsync(int dietId) =>
            _dietCache.ContainsKey(dietId)
                ? _dietCache[dietId]
                : (_dietCache[dietId] = await Connection.Table<Diet>()
                    .FirstOrDefaultAsync(p => p.Id == dietId));

        public async Task<List<Diet>> GetDietsAsync(
            Expression<Func<Diet, bool>> @where, int skip, int take) {
            List<Diet> diets = await Connection.Table<Diet>().Where(where).Skip(skip).Take(take).ToListAsync();
            Thread putDietListIntoCacheThread = new Thread((obj) => {
                List<Diet> list = obj as List<Diet>;
                foreach (var diet in diets)
                {
                    //应该加锁，但是，，，，，
                    if (!_dietCache.ContainsKey(diet.Id))
                    {
                        _dietCache[diet.Id] = diet;
                    }
                }
            });
            putDietListIntoCacheThread.Start(diets);
            return diets;
        }

        private void PutDietListIntoCache(List<Diet> diets) {
            foreach (var diet in diets) {
                if (!_dietCache.ContainsKey(diet.Id)) {
                    _dietCache[diet.Id] = diet;
                }
            }
        }

        public async Task<List<Diet>> GetAllDietsAsync(Expression<Func<Diet, bool>> @where) =>
            await Connection.Table<Diet>().Where(where).ToListAsync();

        public async Task CloseConnection() {
            await _connection.CloseAsync();
            _connection = null;
        }


        //*******构造函数
        public DietStorageService(IPreferenceStorage preference) {
            _preference = preference;
        }
    }
}
